
#include "stdafx.h"

#include "odbc.h"

#include "../CommonFunc/CommonFunc.h"

#pragma comment(lib,"odbc32.lib")

using namespace std;

namespace DDM
{
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// @class Database implementations
DB_ODBC::DB_ODBC() 
{
	m_hEnv = NULL;
	m_hDbc = NULL;
	m_hStmt = NULL;
	m_nConnTimeOut = 10;
	m_iRec = 1;
}
DB_ODBC::~DB_ODBC() 
{ 
    close(); 
}
int DB_ODBC::connect(const char *host, const char *user, const char *passwd, const char *db)
{
    assert(host && user && passwd);

    if (SQLAllocEnv(&m_hEnv) < 0) {
        printk("failed to alloc env!");
        return -1;
    }
	
	if (SQLAllocConnect(m_hEnv, &m_hDbc) < 0) {
        SQLFreeEnv(m_hEnv);
        printk("failed to alloc dbc!");
        return -1;
    }

	if (SQLConnect(m_hDbc,(SQLTCHAR*)host, strlen(host),
                  (SQLCHAR*)user, strlen(user),
                  (SQLCHAR*)passwd, strlen(passwd)) < 0) {
        HandleError(DBERR_DBC);
        SQLFreeConnect(m_hDbc);
        SQLFreeEnv(m_hEnv);
        return -1;
    }

    isConnected = true;
    return 0;
}
void DB_ODBC::close() 
{
    if (!isConnected) return;
	
    SQLFreeStmt(m_hStmt,SQL_CLOSE);
	SQLDisconnect(m_hDbc);
	SQLFreeConnect(m_hDbc);
	SQLFreeEnv(m_hEnv);
}

int DB_ODBC::execute(const char* sql) 
{
	if(!IsConnected()) return (HandleError(DBERR_NOTOPEN), -1);
	
    SQLFreeStmt(m_hStmt, SQL_CLOSE);
	if (SQLAllocStmt(m_hDbc, &m_hStmt) < 0) 
		return (HandleError(DBERR_DBC), -1);
	
	if (SQLSetStmtAttr(m_hStmt, SQL_ATTR_CURSOR_TYPE, 
					   (SQLPOINTER)SQL_CURSOR_DYNAMIC, SQL_IS_INTEGER) < 0)
		return (HandleError(DBERR_STMT), -1);

	if (SQLExecDirect(m_hStmt,(SQLTCHAR*)sql,SQL_NTS) < 0)
		return (HandleError(DBERR_STMT), -1);

    /*if (SQLPrepare(m_hStmt,(SQLTCHAR*)sql,SQL_NTS) < 0)
		return (HandleError(DBERR_STMT), -1);
	
    if (SQLExecute(m_hStmt) < 0)
		return (HandleError(DBERR_STMT), -1);*/

	/*long count = -1;
	if (SQLRowCount(m_hStmt,&count) < 0)
		return (HandleError(DBERR_STMT), -1);
	return count;*/
	return 0;
}

int DB_ODBC::real_execute(const char* sql, vector<DBCol*> *cols)
{
    SQLRETURN ret = 0;

	if(!IsConnected()) return (HandleError(DBERR_NOTOPEN), -1);
	
    SQLFreeStmt(m_hStmt,SQL_CLOSE);
	if (SQLAllocStmt(m_hDbc, &m_hStmt) < 0) 
		return (HandleError(DBERR_DBC), -1);
  
	for (size_t i = 0; i < cols->size(); i++) {
		DBCol *col = cols->at(i);
		SQLINTEGER cb1 = col->len;
		ret = SQLBindParameter(m_hStmt, 1, SQL_PARAM_INPUT, 
							   SQL_C_BINARY, SQL_LONGVARBINARY, 
							   col->len, 0, (SQLPOINTER) col->data, 
							   0, &cb1);
		if (ret < 0) return (HandleError(DBERR_STMT), -1);
	}
    
	if (SQLPrepare(m_hStmt,(SQLTCHAR*)sql,SQL_NTS) < 0) 
		return (HandleError(DBERR_STMT), -1);

	if (SQLExecute(m_hStmt) < 0) 
		return (HandleError(DBERR_STMT), -1);

	long affected_rows = -1;
	if (SQLRowCount(m_hStmt,&affected_rows) < 0)
		return (HandleError(DBERR_STMT), -1);
    if (affected_rows != 1) return (HandleError(DBERR_STMT), -1);

    return 0; 
}
int DB_ODBC::execute_with_result(const char* sql, vector<DBRow*> *records)
{
    if (execute(sql) < 0) return -1;

	ResultSet rslt(&m_hStmt);

	ResultSetMetaData mtdt;
    rslt.GetColInfo(&mtdt);

    int col_num = mtdt.GetColCnt();
    SQLINTEGER len = 0;
    SQLRETURN ret = 0;

    while (SQLFetch(m_hStmt) == SQL_SUCCESS) {

        DBRow *record = new DBRow();
        records->push_back(record);
        for (int col_no = 0; col_no < col_num; col_no++){

            DBCol *pcol = new DBCol();
            record->cols.push_back(pcol);

            ColInfo *col_info = mtdt.GetColumn(col_no);
            switch (col_info->type) {
            case SQL_INTEGER:
            case SQL_BIGINT:
                if (SQLGetData(m_hStmt, col_no+1, SQL_INTEGER, (SQLPOINTER)&pcol->data, sizeof(&pcol->data), &len) < 0)
                    return (HandleError(DBERR_STMT), -1);
                //ret = SQLBindCol(m_hStmt, i+1, SQL_C_ULONG, pcol->data, 0, &len);
                break;
            case SQL_LONGVARBINARY:
                if (SQLGetData(m_hStmt, col_no+1, SQL_C_BINARY, (SQLPOINTER)&pcol->data, 0, &len) != SQL_SUCCESS_WITH_INFO)
                    return (HandleError(DBERR_STMT), -1);
                pcol->len = len;
                pcol->data = new char[pcol->len];
                pcol->alloced = 1;
                if (SQLGetData(m_hStmt, col_no+1, SQL_C_DEFAULT, pcol->data, len, &len) != SQL_SUCCESS)
                    return (HandleError(DBERR_STMT), -1);
                break;
            default:
                return -1;
            }
        }   
    }

	return records->size();
}
int DB_ODBC::Commit() 
{
	return SQLEndTran(SQL_HANDLE_DBC, m_hDbc, SQL_COMMIT);
}

int DB_ODBC::Rollback() 
{
	return SQLEndTran(SQL_HANDLE_DBC,m_hDbc,SQL_ROLLBACK);
}

void DB_ODBC::HandleError(int type) 
{
    switch (type)
    {
    case DBERR_STMT:
        SQLGetDiagRec(SQL_HANDLE_STMT,m_hStmt,m_iRec,
                      m_cState,&m_nErr,(SQLCHAR*)m_cMsg,
                      sizeof(m_cMsg),&m_nMsg);
        break;
    case DBERR_ENV:
        SQLGetDiagRec(SQL_HANDLE_ENV,m_hEnv,m_iRec,
                      m_cState,&m_nErr,(SQLCHAR*)m_cMsg,
                      sizeof(m_cMsg),&m_nMsg);
        break;
    case DBERR_DBC:
        SQLGetDiagRec(SQL_HANDLE_DBC,m_hDbc,m_iRec,
                      m_cState,&m_nErr,(SQLCHAR*)m_cMsg,
                      sizeof(m_cMsg),&m_nMsg);
        break;
	case DBERR_NOTOPEN:
		m_nErr = -DBERR_NOTOPEN;
		strcpy_s(m_cMsg,"Please Connect DB first!");
		break;
    }
}


int ResultSet::GetColInfo(ResultSetMetaData *mtdt) 
{
	SQLSMALLINT column_count = 0;
	if (SQLNumResultCols(*m_hStmt,&column_count) < 0)
        return (HandleError(), -1);
    
	mtdt->SetColCnt(column_count);
	for(int i = 0; i < column_count; i++) {
		ColInfo col;
		int name_length = 0;
		if (SQLDescribeCol(*m_hStmt,(SQLSMALLINT)(i+1),
					(SQLTCHAR*)col.title,
					(SQLSMALLINT)sizeof(col.title),
					(SQLSMALLINT*)&name_length,
					(SQLSMALLINT*)&col.type,
					(SQLUINTEGER*)&col.size,
					(SQLSMALLINT*)&col.decim_size,
					(SQLSMALLINT*)&col.nullable) < 0) {		
			return (HandleError(), -1);			
		}
		mtdt->AddColumn(col);
	}		

	return 0;
}

void ResultSet::HandleError()
{
    SQLGetDiagRec(SQL_HANDLE_STMT,m_hStmt,m_iRec,
                  m_cState,&m_nErr,m_cMsg,
                  sizeof(m_cMsg),&m_nMsg);
}
}
//-----------------------------------------------------------------